import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import cssStyle from './index.module.css'
import arrow from '@/assets/images/arrow-gray.png'

class SelectComponent extends React.PureComponent{
    selectRef = React.createRef()
    listRef = React.createRef()
    sildeBarRef = React.createRef()
    refData = {
        sliderBarMax: 0,
        listMax: 0,
        offsetHeight: 0,
        clientHeight: 0
    }
    requestId = null
    timer = null
    locationListener = null
    setPosition = e => {
        if(this.requestId){
            window.cancelAnimationFrame(this.requestId)
            this.requestId = null
        }
        this.requestId = window.requestAnimationFrame(() => {
            let listRefTranslateY = parseFloat(this.listRef.current.style.transform.split('(')[1])
            let sildeBarRefTranslateY = parseFloat(this.sildeBarRef.current.style.transform.split('(')[1])/100
            let step = parseFloat(this.listRef.current.parentNode.style.lineHeight)*3
            let sildeBarStep = parseFloat(this.listRef.current.parentNode.style.lineHeight)/this.listRef.current.offsetHeight*3
            if((e.type === 'wheel' && e.deltaY > 0) || (e.type === 'DOMMouseScroll' && e.detail > 0)){
                if(parseFloat(sildeBarRefTranslateY.toFixed(4)) < parseFloat((this.refData.sliderBarMax/100).toFixed(4))){
                    listRefTranslateY -= step
                    sildeBarRefTranslateY += Math.abs(this.refData.sliderBarMax/this.refData.listMax)*sildeBarStep
                    if(sildeBarRefTranslateY >= this.refData.sliderBarMax/100){
                        listRefTranslateY = this.refData.clientHeight - this.refData.offsetHeight
                        sildeBarRefTranslateY = this.refData.sliderBarMax/100
                    }
                }else{
                    return
                }
            }else{
                if(sildeBarRefTranslateY > 0){
                    listRefTranslateY += step
                    sildeBarRefTranslateY -= Math.abs(this.refData.sliderBarMax/this.refData.listMax)*sildeBarStep
                    if(sildeBarRefTranslateY < 0){
                        listRefTranslateY = 0
                        sildeBarRefTranslateY = 0
                    }
                }else{
                    return
                }
            }
            this.listRef.current.style.transform = `translateY(${listRefTranslateY}px)`
            this.sildeBarRef.current.style.transform = `translateY(${sildeBarRefTranslateY*100}%)`
        })
        e.preventDefault()
    }
    mouseOver = () => {
        var mousewheelevt = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel"
        if (window.attachEvent){
            this.listRef.current.attachEvent("on" + mousewheelevt, this.setPosition, { passive: false })
        }else if (window.addEventListener){
            this.listRef.current.addEventListener(mousewheelevt, this.setPosition, { passive: false })
        }
    }
    mouseOut = () => {
        window.removeEventListener('mousewheel', this.setPosition, { passive: false })
    }
    clickListener = e => {
        if(!this.selectRef.current.contains(e.target)){
            this.props.toggle(this.props.current)
        }
    }
    remove = () => {
        if(this.locationListener){
            window.cancelAnimationFrame(this.locationListener)
            this.locationListener = null
        }
        this.props.remove()
    }
    setLocation = () => {
        if(this.locationListener){
            window.cancelAnimationFrame(this.locationListener)
            this.locationListener = null
        }
        this.locationListener = window.requestAnimationFrame(() => {
            let { left, top } = this.getLocation()
            this.selectRef.current.style.top = top + 'px'
            this.selectRef.current.style.left = left + 'px'
            this.setLocation()
        })
    }
    getLocation = () => {
        let location = this.props.current.getBoundingClientRect()
        let top = location.bottom
        let currentHeight = this.props.current.children[0].clientHeight
        let bodyHeight = document.documentElement.clientHeight
        if(top + currentHeight > bodyHeight){
            top = top - currentHeight - (location.height) - 2
        }
        return {
            left: location.left,
            top
        }
    }
    show = () => {
        let offsetHeight = this.refData.offsetHeight = this.listRef.current.offsetHeight
        let currentHeight = offsetHeight > 245 ? 245 : offsetHeight
        this.selectRef.current.style.height = currentHeight + 'px'
        this.selectRef.current.style.opacity = 1
        this.refData.clientHeight = currentHeight

        if(offsetHeight === currentHeight){
            this.sildeBarRef.current.style.display = 'none'
        }else{
            this.sildeBarRef.current.style.height = (currentHeight / this.listRef.current.offsetHeight)*100 + '%'
        }
        let sliderBarMax = (offsetHeight - currentHeight)/currentHeight*100
        let listMax = -(offsetHeight - currentHeight)/offsetHeight*100
        this.refData.sliderBarMax = sliderBarMax
        this.refData.listMax = listMax

        this.setLocation()
        setTimeout(() => {
            window.removeEventListener('click', this.clickListener)
            window.addEventListener('click', this.clickListener)
        }, 300)
    }
    componentDidUpdate(){
        if(this.timer){
            return
        }
        if(this.props.open){
            if(this.props.current){
                this.show()
            }
        }else{
            if(this.locationListener){
                window.cancelAnimationFrame(this.locationListener)
                this.locationListener = null
            }
            this.selectRef.current.style.height = 0
            this.selectRef.current.style.opacity = 0
            this.timer = setTimeout(() => {
                this.timer = null
                window.removeEventListener('click', this.clickListener)
            }, 300)
        }
    }
    componentDidMount(){
        this.show()
        window.addEventListener('hashchange', this.remove)
    }
    componentWillUnmount(){
        window.removeEventListener('click', this.clickListener)
        window.removeEventListener('hashchange', this.remove)
    }
    render(){
        return(
            <div
                ref={ this.selectRef }
                className={ cssStyle.selectMain }
                onMouseOver={ this.mouseOver }
                onMouseOut={ this.mouseOut }
            >
                <div
                    className={ cssStyle.content }
                    style={{ lineHeight: '35px' }}
                >
                    <ul ref={ this.listRef } style={{ transform: 'translateY(0%)' }}>
                        {
                            React.Children.map(this.props.children, item => {
                                if(item){
                                    return React.cloneElement(item, {
                                        selectValue: this.props.value,
                                        onChange: this.props.onChange,
                                        multiple: this.props.multiple
                                    })
                                }
                            })
                        }
                    </ul>
                </div>
                <div ref={ this.sildeBarRef } className={ cssStyle.sildeBar } style={{ transform: 'translateY(0%)' }}></div>
            </div>
        )
    }
}

SelectComponent.propTypes = {
    toggle: PropTypes.func,
    current: PropTypes.object,
    open: PropTypes.bool,
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array]),
    remove: PropTypes.func,
    children: PropTypes.array,
    onChange: PropTypes.func,
    multiple: PropTypes.bool
}

class Select extends React.PureComponent{
    constructor(props){
        super(props)
        this.open = false
        this.arrowRef = React.createRef()
        this.selectRef = React.createRef()
        this.requestId = null
        this.selectComponentContainer = null
    }
    toggle = current => {
        this.open = !this.open
        if(this.open){
            this.arrowRef.current.style.transform = 'translateY(-50%) rotate(180deg)'
            current.children[0].style.borderColor = '#409eff'
        }else{
            if(this.arrowRef.current){
                this.arrowRef.current.style.transform = 'translateY(-50%) rotate(0)'
                if(this.props.invalid){
                    current.children[0].style.borderColor = 'red'
                }else{
                    current.children[0].style.borderColor = '#dcdfe6'
                }
            }
        }
        this.toggleSelectComponent(this.open, current)
    }
    onChange = value => {
        let _value
        if(this.props.multiple){
            _value = [...this.props.value]
            let index = _value.indexOf(value)
            if(index > -1){
                _value.splice(index, 1)
            }else{
                _value.push(value)
            }
            setTimeout(() => {
                this.toggleSelectComponent(true, this.selectRef.current)
            })
            
        }else{
            _value = value
            setTimeout(() => {
                this.toggle(this.selectRef.current)
            })
        }
        if(this.props.onChange){
            this.props.onChange(_value)
        }
    }
    toggleSelectComponent = (open, current) => {
        if(open){
            if(!this.selectComponentContainer){
                this.selectComponentContainer = document.createElement('div')
                document.body.appendChild(this.selectComponentContainer)
            }
        }
        ReactDOM.render(
            <SelectComponent {...this.props} onChange={ this.onChange } open={ open } current={ current } toggle={ this.toggle } remove={ this.remove } />,
            this.selectComponentContainer
        )
    }
    remove = () => {
        ReactDOM.unmountComponentAtNode(this.selectComponentContainer)
        document.body.removeChild(this.selectComponentContainer)
        this.selectComponentContainer = null
    }
    render(){
        let invalidStyle = {}
        if(this.props.invalid){
            invalidStyle.borderColor = 'red'
        }
        let value = []
        if(this.props.multiple){
            let multipleValue = this.props.value
            for(let i = 0; i < multipleValue.length; i++){
                React.Children.map(this.props.children, item => {
                    if(item){
                        if(item.props.value == multipleValue[i]){
                            value.push(item.props.label)
                        }
                    }
                })
            }
        }else{
            React.Children.map(this.props.children, item => {
                if(item){
                    if(item.props.value == this.props.value){
                        value.push(item.props.label)
                    }
                }
            })
        }
        return(
            <div className={ cssStyle.select } style={ this.props.style } ref={ this.selectRef }
                onClick={
                    e => {
                        if(this.open){
                            e.stopPropagation()
                        }
                        this.toggle(this.selectRef.current)
                    } 
                }
            >
                <div className={ cssStyle.selectInput } style={{ ...invalidStyle }}>
                    <div>
                        <div style={{ cursor: 'pointer', textOverflow: 'ellipsis' }}>
                            {
                                value.length
                                ?
                                value.map((item, index) => {
                                    return <span key={ index }>{ item }</span>
                                })
                                :
                                <span style={{ color: '#999' }}>{ this.props.placeholder }</span>
                            }
                        </div>
                        <img ref={ this.arrowRef } src={ arrow } alt="arrow" />
                    </div>
                </div>
            </div>
        )
    }
}

Select.propTypes = {
    onChange: PropTypes.func,
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array]),
    placeholder: PropTypes.string,
    children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    multiple: PropTypes.bool,
    style: PropTypes.object,
    invalid: PropTypes.bool
}

function Option(props){
    let checked = false
    if(props.multiple){
        if(props.selectValue.indexOf(props.value) > -1){
            checked = true
        }
    }else{
        if(props.selectValue == props.value){
            checked = true
        }
    }
    return(
        <li
            onClick={ e => { props.onChange(props.value, e) } }
            style={{
                color: checked ? '#409eff' : '#606266'
            }}
        >
            { props.label }
        </li>
    )
}

Option.propTypes = {
    onChange: PropTypes.func,
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    label: PropTypes.string,
    multiple: PropTypes.bool,
    selectValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array])
}

export {
    Select,
    Option
}